<?php

namespace app\bll;

use core\lib\upload\UploadFile as LibUploadFile;
use Exception;
use hulang\tool\FileHelper;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Melihovv\Base64ImageDecoder\Base64ImageEncoder;
use PhpZip\ZipFile;
use support\Db;
use Throwable;
use app\lib\installer\General as LibInstallerGeneral;


/**
 * 事件组件
 */
class ComponentEvent
{


    /**
     * 查找一个事件
     *
     * @param integer $id
     * @return array
     */
    public function find(int $id): array
    {
        $data = Db::table('frame_component_event')
            ->where('id', '=', $id)
            ->first();
        return $data === null ? [] : get_object_vars($data);
    }


    /**
     * 查找所有事件
     *
     * @return array
     */
    public function select():array
    {
        return Db::table('frame_component_event')
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
    }

    /**
     * 根据ident查询
     *
     * @param string $ident
     * @return array
     */
    public function findByIdent(string $ident): array
    {
        $data = Db::table('frame_component_event')
            ->where('ident', '=', $ident)
            ->first();
        return $data === null ? [] : get_object_vars($data);
    }
    /**
     * 安装
     *
     * @return void
     */
    public function install()
    {

        $libInstallerGeneral = new LibInstallerGeneral();
        $installres =  $libInstallerGeneral->install(
            $this->getEventPath(),
            ['describe.php']
        );

        //读写描述文件写入数据库
        $describe = $this->getDescribe($installres['foldername']);
        $methods = $describe['method'];
        unset($describe['method']);

        Db::beginTransaction();
        try {
            $eventid = Db::table('frame_component_event')
                ->insertGetId($describe);
            foreach ($methods as $key => $method) {
                $methods[$key]['eventid'] = $eventid;
            }

            Db::table('frame_component_event_method')
                ->insert($methods);

            Db::commit();
        } catch (Throwable $ex) {
            Db::rollBack();
            throw $ex;
        }
    }



    /**
     * 手动升级
     *
     * @param integer $id
     * @return void
     */
    public function handleUpgrade(int $id)
    {
        try {
            $event = $this->find($id);
            $libInstallerGeneral = new LibInstallerGeneral();
            $res = $libInstallerGeneral->offlineUpgrade(
                $event['ident'],
                $event['version'],
                $this->getEventPath(),
                ['describe.php']
            );

            $this->upgrade($event['ident'], $event['id'], $res['nextVersion']);
        } catch (Exception $ex) {
            throw $ex;
        }
    }


    /**
     * 在线升级
     *
     * @param integer $id
     * @return void
     */
    public function onlineUpgrade(int $id)
    {
        $event = $this->find($id);
        if (! $event) {
            throw new Exception('事件组不存在');
        }

        if (trim($event['upgrade']) == '') {
            throw new Exception('事件组没有升级地址');
        }

        try {
            $upgrade        = $event['upgrade'];
            if ($upgrade != '') {
                $libInstallerGeneral = new LibInstallerGeneral();
                $res = $libInstallerGeneral->onlineUpgrade(
                    $upgrade,
                    $event['version'],
                    $this->getEventPath(),
                    ['describe.php']
                );
                $this->upgrade($event['ident'], $event['id'], $res['nextVersion']);
            }
        } catch (Exception $ex) {
            throw $ex;
        }
    }

    /**
     * 实际升级[ 更新数据库]
     *
     * @param string $ident 标识
     * @param integer $eventgroupid 事件组id
     * @param string $nextVersion 下一个版本号
     * @return void
     */
    private function upgrade(
        string $ident,
        int $eventid,
        string $nextVersion
    ) {
        $describe                = $this->getDescribe($ident);
        $describe['nextversion'] = '';
        $describe['version']     = $nextVersion;

        $methods = $describe['method'];
        unset($describe['method']);

        Db::beginTransaction();
        try {

            Db::table('frame_component_event')
                ->where('id', '=', $eventid)
                ->update($describe);



            Db::table('frame_component_event_method')
                ->where('eventid', '=', $eventid)
                ->delete();

            foreach ($methods as $key => $method) {
                $methods[$key]['eventid'] = $eventid;
            }

            Db::table('frame_component_event_method')
                ->insert($methods);


            Db::commit();
        } catch (Throwable $ex) {
            Db::rollBack();
            throw $ex;
        }
    }



    /**
     * 卸载
     *
     * @param integer $id
     * @return void
     */
    public function unInstall(int $id)
    {
        Db::beginTransaction();
        try {
            $eventgroup = $this->find($id);

            //删掉挂载钩子的事件组
            Db::table('frame_hook_event')
                ->where('event', 'like', $eventgroup['ident'] . '/')
                ->delete();


            //删掉事件组
            Db::table('frame_component_event')
                ->where('id', '=', $id)
                ->delete();

            //删掉具体事件
            Db::table('frame_component_event_method')
                ->where('eventid', '=', $id)
                ->delete();

            //删掉文件夹
            FileHelper::delDir($this->getEventPath() . $eventgroup['ident']);
            Db::commit();
        } catch (Throwable $ex) {
            Db::rollBack();
            throw $ex;
        }
    }



    /**
     * 获取描述文件内容
     *
     * @param string $path
     * @return array
     */
    private function getDescribe(string $eventfoldername): array
    {
        $describe          = include $this->getEventPath() . $eventfoldername . '/describe.php';
        $describe['ident'] = $eventfoldername;
        $logoimg           = $this->getEventPath() . $eventfoldername . '/logo.png';
        if ($logoimg) {
            $imageEncoder = Base64ImageEncoder::fromFileName($logoimg,  ['jpeg', 'png', 'gif']);
            $describe['logo'] =  $imageEncoder->getDataUri();
        }
        return $describe;
    }


    /**
     * 获取事件文件夹的物理路径
     *
     * @return string
     */
    private function getEventPath(): string
    {
        return  base_path() . '/component/event/';
    }





    /**
     * 检查升级
     *
     * @param integer|array $extend_or_id
     * @return boolean
     */
    public function checkUpgrade(int|array $event_or_id): bool
    {
        $event = $event_or_id;
        if (is_int($event_or_id)) {
            $event = $this->find($event_or_id);
        }
        $upgrade = $event['upgrade'];
        if ($upgrade == '') {
            return false;
        }

        $libInstallerGeneral = new LibInstallerGeneral();
        $nextVersion = $libInstallerGeneral->getNextUpgradeVersion($upgrade, $event['version']);

        if ($nextVersion) {
            Db::table('frame_component_event')
                ->where('id', '=', $event['id'])
                ->update([
                    'nextversion' => $nextVersion['nextVersion']
                ]);
            return true;
        }

        Db::table('frame_component_event')
            ->where('id', '=', $event['id'])
            ->update([
                'nextversion' => ''
            ]);
        return false;
    }



    /**
     * 检查所有元素是否有版本更新
     *
     * @return void
     */
    public function checkAllUpgrade()
    {
        $events = $this->select();
        foreach ($events as $event) {
            $this->checkUpgrade($event);
        }
    }



    /**
     * 获取级联数据
     *
     *
     * @param bool $json 是否将结果转换为 JSON 格式，默认为 `true`。
     * @return array|string 如果 `$json` 为 `true`，则返回 JSON 格式的字符串；否则返回数组。
     */
    public function cascader(bool $json = true): array|string
    {
        // 从 'frame_component_extend_group' 表中获取所有组数据，并将查询结果中的对象转换为数组
        $events = Db::table('frame_component_event')
            ->get(['id', 'title', 'ident'])
            ->map(function ($value) {
                // 将查询结果中的每个对象转换为数组
                return (array)$value;
            })
            ->toArray();

        // 遍历每个组，为其添加对应的子组件数据
        foreach ($events as $key => $event) {
            // 查询该组的所有子组件数据，并将对象转换为数组

            $methods = Db::table('frame_component_event_method')
                ->where('eventid', '=', $event['id'])
                ->get()
                ->map(function ($value) {
                    // 将查询结果中的每个对象转换为数组
                    return (array)$value;
                })
                ->toArray();
            foreach ($methods as $mkey => $method) {
                $methods[$mkey]['cascaderid'] = $event['id'] . '_' . $method['id'];
            }
            $events[$key]['cascaderid'] = $event['id'];
            $events[$key]['children']   = $methods;
        }

        // 根据 $json 参数决定返回格式，若为 true 则返回 JSON 字符串，否则返回数组
        return $json ? json_encode($events, JSON_UNESCAPED_UNICODE) : $events;
    }


    /**
     * 分页查询
     *
     * @param string $title
     * @param integer $pagesize
     * @return LengthAwarePaginator
     */
    public function paginator(
        string $title = '',
        string $ident = '',
        int $pagesize = 20
    ): LengthAwarePaginator {
        return  Db::table('frame_component_event')
            ->when($title != '', function ($query) use ($title) {
                return $query->where('title', 'like', '%' . $title . '%');
            })
            ->when($ident != '', function ($query) use ($ident) {
                return $query->where('ident', 'like', '%' . $ident . '%');
            })
            ->orderBy('id', 'desc')
            ->paginate($pagesize);
    }
}
